package scu.maqiang.fes;

import scu.maqiang.mesh.Mesh2T3;
import scu.maqiang.mesh.T3Type;
import scu.maqiang.numeric.Direct;
import scu.maqiang.numeric.MVO;
import scu.maqiang.numeric.Pair;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 周期边界有限元空间
 */
public class FES2T31P extends FES2T31 {
    public FES2T31P(Mesh2T3 mesh2D, Map<Direct, Pair<Integer, Integer>> myMap) {
        super(mesh2D);
        int n = myMap.size();
        int[][][] nodePair = new int[n][][];
        int idx = 0;
        int nv = mesh.getNv();
        int[] marked = new int[nv];
        Arrays.fill(marked, -1);  //-1表示非周期边界结点
        for(var entry: myMap.entrySet()) {
            var dire = entry.getKey();
            var labelPair = entry.getValue();
            nodePair[idx] = mesh.extractPeriodicBoundary(labelPair.getFirst(), labelPair.getSecond(), dire);
            for(int i = 0, ni = nodePair[idx].length; i < ni; i++) {
                for(int j = 0, nj = nodePair[idx][i].length; j < nj; j++) {
                    marked[nodePair[idx][i][j]] = -2;
                }
            }
            idx++;
        }

//        System.out.println(Arrays.toString(marked));
        idx = 0;
        for(int i = 0; i < nv; i++) {
            if (marked[i] == -1) {  //如果不是周期边界结点，则直接赋值
                marked[i] = idx++;
            } else if(marked[i] > 0) {
                continue;
            } else {
                for(int j = 0; j < n; j++) {
                    int[][] pair = nodePair[j];
                    for(int k = 0, nk = pair.length; k < nk; k++) {
                        int left = pair[k][0], right = pair[k][1];
                        if (i == left || i == right) {
                            if (marked[left] < 0) {
                                if (marked[right] < 0) {
                                    marked[left] = marked[right] = idx++;
                                } else {
                                    marked[left] = marked[right];
                                }
                                break;
                            } else if (marked[right] < 0) {
                                marked[right] = marked[left];
                                break;
                            }
                        }
                    }
                }
            }
        }
        ndof = MVO.max(marked) + 1;
        for(int i = 0; i < nv; i++) {
            for(int j = 0; j < dofPerNode; j++) {
                dofs[i][j] = dofPerNode * marked[i] + j;
            }
        }
        //System.out.println(MVO.toString(dofs));
//        for(int i = 0; i < mesh.getNt(); i++) {
//            int[] ele = mesh.getElement(i);
//            int[] dof = getDofFromDofs(ele);
//            System.out.println(Arrays.toString(dof));
//        }
    }

    public double[] solutionToMesh(double[] solution) {
        int nv = mesh.getNv();
        double[] meshSolution = new double[nv];
        for(int i = 0; i < nv; i++) {
            for(int j = 0; j < dofPerNode; j++) {
                meshSolution[i * dofPerNode + j] = solution[dofs[i][j]];
            }
        }
        return meshSolution;
    }

    public static void main(String[] args) {
        Mesh2T3 mesh = new Mesh2T3().square2D(4, 4, T3Type.Left);
        HashMap<Direct, Pair<Integer, Integer>> myMap = new HashMap<>();
        myMap.put(Direct.Y, new Pair<>(1, 3));
        myMap.put(Direct.X, new Pair<>(2, 4));
        FES2T31P fesP = new FES2T31P(mesh, myMap);
        fesP.printDofs();
        System.out.println(fesP.GetNdof());
    }
}
